PostMessages Guide
Listen for real-time iFrame events to track payment form lifecycle, field validation, and transaction results — and optionally trigger form submission from your parent page.
Before You Begin
| Requirement | Details |
|---|---|
| Payment iFrame | An embedded Connected Payments payment iFrame already set up on your page |
| HTTPS | Parent page served over HTTPS |
| JavaScript | Basic event handling knowledge |
PostMessages are client-side only and depend on the customer's browser. Always use server-to-server notifications as the authoritative source for payment results.
Code samples: Code samples within this document are provided for reference purposes only and are not intended for production use.
How It Works
- Customer loads your page containing the Connected Payments payment iFrame
- The iFrame emits PostMessages as state changes (form loaded, field touched, submitted)
- Your
messageevent listener receives and routes each event - You respond in the UI (show spinner, resize iFrame, enable submit button)
- Payment outcome arrives — verify result server-side via notifications
Quick Start
- 1. Listen
- 2. Route
- 3. Handle
- 4. Resize
Register a Message Listener
Add this to your page as early as possible — before the iFrame loads.
window.addEventListener("message", receiver, false);
function receiver(event) {
if (!event.origin.includes("connectedpayments.commbank.com.au")) {
console.warn("Rejected PostMessage from:", event.origin);
return;
}
try {
const data = JSON.parse(event.data);
handlePaymentEvent(data);
} catch (error) {
console.error("Failed to parse PostMessage:", error);
}
}
Route Incoming Events
function handlePaymentEvent(data) {
if (data.resize) {
handleResizeEvent(data);
} else if (data.errors !== undefined) {
handleFieldInteraction(data);
} else if (data.info) {
handleInfoEvent(data);
}
}
Handle Info Events
function handleInfoEvent(data) {
switch (data.info) {
case "The payment form is loading.":
showLoadingSpinner();
break;
case "The payment form has rendered.":
hideLoadingSpinner();
break;
case "The payment form was submitted":
showProcessingState();
break;
case "Payment outcome":
showPaymentResult(data);
break;
case "Redirecting response back to client defined endpoint.":
onRedirect(data);
break;
default:
console.log("Unhandled info event:", data.info);
}
}
Resize the iFrame Automatically
function handleResizeEvent(data) {
const iframe = document.getElementById("paymentFrame");
if ("height" in data.resize) {
iframe.height = parseInt(data.resize.height);
}
if ("width" in data.resize) {
iframe.width = parseInt(data.resize.width);
}
}
Event Reference
- Lifecycle
- Resize & Fields
- Validation
- Results
Lifecycle Events
Events emitted as the payment form moves through its standard states.
| Event | info Value | Fields | Trigger |
|---|---|---|---|
| Loading | "The payment form is loading." | merchReference | Initial request to Connected Payments (fetching configuration) |
| Rendered | "The payment form has rendered." | merchReference | Configuration retrieved, payment form rendered to DOM |
| Submitted | "The payment form was submitted" | merchReference, name | User submits the payment form |
Resize Event
Triggered when the iFrame content changes size (responsive layout, device rotation, form state change).
Width typically remains constant. Height adjusts dynamically based on content — always handle resize events for embedded integrations.
Field Interaction Event
Triggered when a user interacts with any form field (focus, blur, or input).
Use isValid to enable or disable an external submit button.
Result Events
| Event | info Value | Fields | Trigger |
|---|---|---|---|
| Payment outcome | "Payment outcome" | merchReference, name | Payment processing is complete |
| Hosted response | "The hosted response has rendered." | merchReference, name, cardToken | Response page rendered inside the iFrame |
| Redirect | "Redirecting response back to client defined endpoint." | merchReference, name, cardToken | User is about to be redirected back to your site |
Do not rely solely on PostMessage events for payment processing decisions. Always use server-to-server notifications as the authoritative source.
Event Summary
| Event | info Value | Direction | Key Fields |
|---|---|---|---|
| Form loading | "The payment form is loading." | Receive | merchReference |
| Form rendered | "The payment form has rendered." | Receive | merchReference |
| Form submitted | "The payment form was submitted" | Receive | merchReference, name |
| iFrame resize | "resize" | Receive | resize.height, resize.width |
| Field interaction | (no info; has errors key) | Receive | isValid, formState, stateOfFields |
| reCAPTCHA failed | "Google recaptcha failed the validation" | Receive | merchReference, name |
| reCAPTCHA success | "Google recaptcha validation successful" | Receive | merchReference, name |
| Confirmation rendered | "The confirmation form has rendered." | Receive | merchReference, name |
| Confirmation submitted | "The confirmation form has been submitted." | Receive | merchReference, name |
| Payment outcome | "Payment outcome" | Receive | merchReference, name |
| Hosted response | "The hosted response has rendered." | Receive | merchReference, name, cardToken |
| Response redirect | "Redirecting response back to client defined endpoint." | Receive | merchReference, name, cardToken |
| External submit | (no info) | Send | action: "submit" |
Sending Events
External Form Submission
Submit the payment form from your parent page to coordinate submission of your own form alongside the payment form.
You have a larger checkout form and want a single submit button to trigger both your form and the payment form simultaneously.
Payload:
{
"action": "submit"
}
Implementation:
function submitPaymentIframe() {
const paymentFrame = document.getElementById("paymentFrame");
paymentFrame.contentWindow.postMessage(
JSON.stringify({ action: "submit" }),
"*",
);
}
Best practice: Only call this when isValid is true. Monitor field interaction events to track form state before enabling your submit button.
let formIsValid = false;
function handleFieldInteraction(data) {
formIsValid = data.isValid;
const submitButton = document.getElementById("externalSubmitButton");
submitButton.disabled = !formIsValid;
}
document
.getElementById("externalSubmitButton")
.addEventListener("click", () => {
if (formIsValid) {
submitPaymentIframe();
}
});
Complete Example
View full implementation
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Checkout</title>
</head>
<body>
<div id="loadingSpinner">Loading payment form...</div>
<iframe
id="paymentFrame"
src="YOUR_ENTERPRISESECURE_URL"
width="100%"
height="600"
frameborder="0"
>
</iframe>
<button id="externalSubmitButton" disabled>Pay Now</button>
<script>
let formIsValid = false;
window.addEventListener("message", receiver, false);
function receiver(event) {
if (!event.origin.includes("connectedpayments.commbank.com.au")) {
console.warn("Rejected PostMessage from:", event.origin);
return;
}
try {
const data = JSON.parse(event.data);
routeEvent(data);
} catch (error) {
console.error("Failed to parse PostMessage:", error, event.data);
}
}
function routeEvent(data) {
if (data.resize) {
handleResizeEvent(data);
} else if (data.errors !== undefined) {
handleFieldInteraction(data);
} else if (data.info) {
handleInfoEvent(data);
}
}
function handleInfoEvent(data) {
switch (data.info) {
case "The payment form is loading.":
document.getElementById("loadingSpinner").style.display = "block";
break;
case "The payment form has rendered.":
document.getElementById("loadingSpinner").style.display = "none";
break;
case "The payment form was submitted":
document.getElementById("externalSubmitButton").disabled = true;
document.getElementById("externalSubmitButton").textContent =
"Processing...";
break;
case "Payment outcome":
console.log("Payment outcome received for:", data.merchReference);
break;
case "The hosted response has rendered.":
console.log("Hosted response rendered, cardToken:", data.cardToken);
break;
case "Redirecting response back to client defined endpoint.":
console.log("Redirecting, cardToken:", data.cardToken);
break;
case "The confirmation form has rendered.":
console.log("Confirmation form ready");
break;
case "The confirmation form has been submitted.":
console.log("Confirmation submitted");
break;
case "Google recaptcha failed the validation":
console.warn("reCAPTCHA failed");
break;
case "Google recaptcha validation successful":
console.log("reCAPTCHA passed");
break;
default:
console.log("Unhandled info event:", data.info);
}
}
function handleResizeEvent(data) {
const iframe = document.getElementById("paymentFrame");
if ("height" in data.resize)
iframe.height = parseInt(data.resize.height);
if ("width" in data.resize) iframe.width = parseInt(data.resize.width);
}
function handleFieldInteraction(data) {
formIsValid = data.isValid;
document.getElementById("externalSubmitButton").disabled = !formIsValid;
}
document
.getElementById("externalSubmitButton")
.addEventListener("click", () => {
if (formIsValid) {
const paymentFrame = document.getElementById("paymentFrame");
paymentFrame.contentWindow.postMessage(
JSON.stringify({ action: "submit" }),
"*",
);
}
});
</script>
</body>
</html>